package guggaTests.tasks.containers
{
	import flash.events.Event;
	
	import flexunit.framework.TestCase;
	
	import gugga.eventHelpers.EventDescriptor;
	import gugga.tasks.ExecuteAsyncMethodTask;
	import gugga.tasks.FictiveTask;
	import gugga.tasks.FrameWaitingTask;
	import gugga.tasks.ITask;
	import gugga.tasks.SingleExecutionTask;
	import gugga.tasks.containers.TaskSequence;
	import gugga.tasks.events.TaskEvent;
	import gugga.tasks.events.TasksContainerEvent;
	import gugga.utils.DoLaterUtil;

	public class TaskSequenceTest extends TestCase
	{
		private var mTaskSequenceStarted  : Boolean;
		private var mTaskSequence : TaskSequence;
		private var mTaskSequenceIsRunning : Boolean;
		private var mTaskSequenceCompleted : Boolean;
		private var mTaskInterrupted : Boolean = false;
		
		private var mTaskAddedCounter : Number = 0;
		private var mTaskRemovedCounter : Number = 0;
		
		public function TaskSequenceTest(methodName:String=null)
		{
			super(methodName);
		}
	
		public function testConstructor() : void
		{
			var taskSequence:TaskSequence = new TaskSequence();
			
			assertNotNull("taskSequence should not be null after creation", taskSequence);
			assertNotUndefined("taskSequence should not be undefined after creation", taskSequence);
			assertFalse(
					"taskSequence has not started yet, so isRunning() should be false", 
					taskSequence.isRunning());
		}
		
		public override function setUp() : void
		{
			super.setUp();
			
			mTaskSequence = new TaskSequence();
			mTaskSequenceIsRunning = false;
			mTaskSequenceCompleted = false;
			mTaskSequenceStarted = false;
			mTaskAddedCounter = 0;
			mTaskRemovedCounter = 0;
			mTaskInterrupted = false;
			mIndividualTaskInterrupted = false;
		}
		
		public function testIsRunningAfterStart() : void
		{
			var taskSequence:TaskSequence = new TaskSequence();
			
			var fictiveTask:FictiveTask = new FictiveTask();

			var eventDescriptor:EventDescriptor = new EventDescriptor(fictiveTask, TaskEvent.COMPLETED);
			var asyncTask:ExecuteAsyncMethodTask = ExecuteAsyncMethodTask.create(
					eventDescriptor,
					emptyMethod, 
					[],
					this);
			
			taskSequence.addTask(asyncTask);
			taskSequence.start();

			assertTrue(
					"asyncTask has not completed yet, so taskSequence should still be in running state",
					taskSequence.isRunning());
		}
		
		private function emptyMethod() : void
		{
			// THIS METHOD SHOULD BE EMPTY!!!
		}
		
		public function testIsRunningAfterCompleted() : void
		{
	 		mTaskSequence.addEventListener(TaskEvent.START, onTaskSequenceStarted);
			mTaskSequence.addEventListener(TaskEvent.COMPLETED, addAsync(assertTaskSequenceCompleted, 200));
			
			var fictiveTask : FictiveTask = new FictiveTask();
					
			mTaskSequence.addTask(fictiveTask);
			
			mTaskSequence.start();
			
			assertTrue("TaskSequence should be started", mTaskSequenceStarted);
		}
		
		public function testStartWithSingleTask() : void
		{
			mTaskSequence.addEventListener(TaskEvent.START, onTaskSequenceStarted);
			
			var fictiveTask:FictiveTask = new FictiveTask();
					
			mTaskSequence.addTask(fictiveTask);
			
			mTaskSequence.start();
			
			assertTrue("TaskSequence should be started", mTaskSequenceStarted);
		}
		
		public function testStartWithoutTasks() : void
		{
			mTaskSequence.addEventListener(TaskEvent.START, onTaskSequenceStarted);
			
			mTaskSequence.start();
			
			assertTrue("TaskSequence should be started", mTaskSequenceStarted);
		}
		
		public function testComplete() : void
		{
			mTaskSequence.addEventListener(TaskEvent.COMPLETED, addAsync(assertTaskSequenceCompleted, 200));
			
			var fictiveTask : FictiveTask = new FictiveTask();
					
			mTaskSequence.addTask(fictiveTask);
			
			mTaskSequence.start();
		}
		
		public function testCompleteWithoutTasks() : void
		{
			mTaskSequence.addEventListener(TaskEvent.COMPLETED, addAsync(assertTaskSequenceCompleted, 200));
			
			mTaskSequence.start();
		}
		
		/**
		 * These flags are used from testSequentialCompletion to check 
		 * completion order 
		*/		
		private var mFirstTaskCompleted : Boolean = false;
		private var mSecondTaskCompleted : Boolean = false;
		private var mThirdTaskCompleted : Boolean = false;
		
		public function testSequentialCompletion() : void
		{
			var firstTask : ITask = new FictiveTask();
			var secondTask : ITask = new FrameWaitingTask(1);
			var thirdTask : ITask = new SingleExecutionTask(new FictiveTask());
			
			firstTask.addEventListener(TaskEvent.COMPLETED, onFirstTaskCompleted);
			secondTask.addEventListener(TaskEvent.COMPLETED, onSecondTaskCompleted);
			thirdTask.addEventListener(TaskEvent.COMPLETED, onThirdTaskCompleted);
			
			mTaskSequence.addTask(firstTask);
			mTaskSequence.addTask(secondTask);
			mTaskSequence.addTask(thirdTask);
			
			mTaskSequence.addEventListener(TaskEvent.COMPLETED, addAsync(assertSequentialCompletion, 1000));
			
			mTaskSequence.start();
			
		}
		
		private function onFirstTaskCompleted(aEvent : Event) : void 
		{
			if(!(mSecondTaskCompleted && mThirdTaskCompleted))
			{
				mFirstTaskCompleted = true;
			}
		}
		
		private function onSecondTaskCompleted(aEvent : Event) : void
		{
			if(mFirstTaskCompleted && !mThirdTaskCompleted)
			{
				mSecondTaskCompleted = true;
			}			
		}
		
		private function onThirdTaskCompleted(aEvent : Event) : void
		{
			if(mFirstTaskCompleted && mSecondTaskCompleted)
			{
				mThirdTaskCompleted = true;
			}
		}
		
		private function assertSequentialCompletion(aEvent : Event) : void
		{
			assertTrue("FirstTask should complet first", mFirstTaskCompleted);
			assertTrue("SecondTask should complete second", mSecondTaskCompleted);
			assertTrue("ThirdTask should complete third", mThirdTaskCompleted);
		}
		
		public function testAddTaskWhenNotStarted() : void
		{
			mTaskSequence.addEventListener(TasksContainerEvent.TASKS_ADDED, onTaskAdded);
						
			var fictiveTask1:FictiveTask = new FictiveTask();
			var fictiveTask2:FictiveTask = new FictiveTask();
			
			mTaskSequence.addTask(fictiveTask1);
			
			var tasks:Array = mTaskSequence.getAllTasks();
			assertEquals("taskSequence should contain only 1 task after single addTask()", 1, tasks.length);
			assertTrue("these tasks should be the same", tasks[0] is FictiveTask);
						
			mTaskSequence.addTask(fictiveTask2);
			
			tasks = mTaskSequence.getAllTasks();
			assertEquals("taskSequence should contain only 2 tasks after this addTask()", 2, tasks.length);
			assertTrue("these tasks should be the same", tasks[0] is FictiveTask);
			assertEquals("these tasks should be the same", tasks[0], fictiveTask1)
			assertTrue("these tasks should be the same", tasks[1] is FictiveTask);
			assertEquals("these tasks should be the same", tasks[1], fictiveTask2)
			
			/* try to add already added task:
			
			mTaskSequence.addTask(fictiveTask2);
			tasks = mTaskSequence.getAllTasks();
			assertEquals(
					"taskSequence should contain only 2 tasks after trying to add already added task", 
					2, 
					tasks.length);
			assertTrue("these tasks should be the same", tasks[0] is FictiveTask);
			assertEquals("these tasks should be the same", tasks[0], fictiveTask1)
			assertTrue("these tasks should be the same", tasks[1] is FictiveTask);
			assertEquals("these tasks should be the same", tasks[1], fictiveTask2) */
		}
		
		public function testAddTaskAfterCompleted() : void
		{
			var fictiveTask1:FictiveTask = new FictiveTask();
					
			var taskSequence:TaskSequence = new TaskSequence();
			taskSequence.addEventListener(TaskEvent.COMPLETED, addAsync(assertCompletedAndTestAddTask, 200));
					
			taskSequence.addTask(fictiveTask1);
			
			taskSequence.start();
		}
		
		private function assertCompletedAndTestAddTask(aEvent : Event) : void
		{
			var taskSequence : TaskSequence = TaskSequence(aEvent.target);
			var fictiveTask2:FictiveTask = new FictiveTask();
			
			taskSequence.addTask(fictiveTask2);
			
			var tasks:Array = taskSequence.getAllTasks();			
			assertFalse("TaskSequence should be completed and not running", taskSequence.isRunning());
			assertEquals("taskSequence should contain 2 tasks after this addTask()", 2, tasks.length);
			assertEquals("these tasks should be the same", tasks[1], fictiveTask2);
		}
		
		public function testAddTaskWhenRunning() : void
		{
			var fictiveEventSource : FictiveTask = new FictiveTask();
			var eventDescriptor : EventDescriptor = new EventDescriptor(fictiveEventSource, TaskEvent.COMPLETED);
			
			var asyncTask : ExecuteAsyncMethodTask = 
				ExecuteAsyncMethodTask.create(eventDescriptor, emptyMethod, [], this);
			
			var taskSequence:TaskSequence = new TaskSequence();
			
			taskSequence.addEventListener(TaskEvent.START, addAsync(assertIsRunningAndAddTask, 100));
			taskSequence.addEventListener(TaskEvent.COMPLETED, addAsync(assertCompletedAndTasksWereAdded, 500));
					
			taskSequence.addTask(asyncTask);

			taskSequence.start();
			
			DoLaterUtil.doLater(fictiveEventSource.start, [], 2);
		}
		
		private function assertIsRunningAndAddTask(aEvent : Event) : void
		{
			var taskSequence : TaskSequence = TaskSequence(aEvent.target);
			var fictiveTask : FictiveTask = new FictiveTask();
			
			assertTrue("TaskSequence should be in running state now", taskSequence.isRunning());
		
			taskSequence.addTask(fictiveTask);
			
			var tasks : Array = taskSequence.getAllTasks();
			assertEquals("taskSequence should contain 2 tasks after this addTask()", 2,	tasks.length);
			assertTrue("these tasks should be the same", tasks[0] is ExecuteAsyncMethodTask);
			assertEquals("these tasks should be the same", tasks[1], fictiveTask);
		}
		
		private function assertCompletedAndTasksWereAdded(aEvent : Event) : void
		{
			var taskSequence : TaskSequence = TaskSequence(aEvent.target);
			var tasks : Array = taskSequence.getAllTasks();
			
			assertEquals("TaskSequence should contain two elements", 2, tasks.length);
		}
		
		public function testAddTaskAfter() : void
		{
			var sequence : TaskSequence = new TaskSequence();
			
			var task1 : FictiveTask = new FictiveTask();
			var task2 : FictiveTask = new FictiveTask();
			var newTask : FictiveTask = new FictiveTask();
			
			sequence.addTask(task1);
			sequence.addTask(task2);
			
			sequence.addTaskAfter(task1, newTask);
			
			var tasks : Array = sequence.getAllTasks();
			
			for(var i:Number = 0; i < tasks.length; i++)
			{
				if(tasks[i] === task1)
				{
					assertEquals("these tasks should be equal", tasks[i+1], newTask);
				}
			}
		}
		
		/* public function testAddTaskAfterNonExistentTask() : void
		{
			var sequence : TaskSequence = new TaskSequence();
			
			var task1 : FictiveTask = new FictiveTask();
			var task2 : FictiveTask = new FictiveTask();
			var task3 : FictiveTask = new FictiveTask();
			
			var nonExistentTask : FictiveTask = new FictiveTask();
			
			sequence.addTask(task1);
			sequence.addTask(task2);
			
			sequence.addTaskAfter(nonExistentTask, task3);
			
			var tasks : Array = sequence.getAllTasks();
			
			var assertionBool : Boolean = true; 
			
			for(var i:Number = 0; i < tasks.length; i++)
			{
				if(tasks[i] === task3)
				{
					assertionBool = false;
				}
			}
			
			assertTrue("this task should't been added after nonexistent one, but it is", assertionBool);
		}  */
		
		public function testAddTaskBefore() : void
		{
			var sequence : TaskSequence = new TaskSequence();
			
			var task1 : FictiveTask = new FictiveTask();
			var task2 : FictiveTask = new FictiveTask();
			var newTask : FictiveTask = new FictiveTask();
			
			sequence.addTask(task1);
			sequence.addTask(task2);
			
			sequence.addTaskBefore(task2, newTask);
			
			var tasks : Array = sequence.getAllTasks();
			
			for(var i:Number = 0; i < tasks.length; i++)
			{
				if(tasks[i] === task2)
				{
					assertEquals("these tasks should be equal",	tasks[i-1],	newTask);
				}
			}
		}
		
		/* public function testAddTaskBeforeNonExistentTask ()
		{
			var sequence : TaskSequence = new TaskSequence();
			
			var task1 = new FictiveTask();
			var task2 = new FictiveTask();
			var task3 = new FictiveTask();
			
			var nonExistentTask = new FictiveTask();
			
			sequence.addTask(task1);
			sequence.addTask(task2);
			
			sequence.addTaskBefore(nonExistentTask, task3);
			
			var tasks : Array = sequence.getAllTasks();
			
			var assertionBool : Boolean = true; 
			
			for(var i:Number = 0; i < tasks.length; i++)
			{
				if(tasks[i] === task3)
				{
					assertionBool = false;
				}
			}
			
			assertTrue(
				"this task should't been added before nonexistent one, but it is",
				assertionBool
			);
		}  */
		
		public function testGetTaskAfter () : void
		{
			var sequence : TaskSequence = new TaskSequence();
			
			var task1 : ITask = new FictiveTask();
			var task2 : ITask = new FictiveTask();
			
			sequence.addTask(task1);
			sequence.addTask(task2);
			
			var newTask : ITask;
			
			newTask = sequence.getTaskAfter(task1);
			
			assertEquals("these tasks should be equal", newTask, task2);
		}
		
		/* public function testGetTaskAfterNonExistentOne() : void
		{
			var sequence : TaskSequence = new TaskSequence();
			
			var task1 : ITask = new FictiveTask();
			var task2 : ITask = new FictiveTask();
			
			sequence.addTask(task1);
			sequence.addTask(task2);
			
			var newTask : ITask;
			var nonExistentTask : ITask = new FictiveTask();
			
			newTask = sequence.getTaskAfter(nonExistentTask);
			
			assertUndefined("expected result should be undefined", newTask);
		} */
		
		public function testGetTaskBefore() : void
		{
			var sequence : TaskSequence = new TaskSequence();
			
			var task1 : ITask = new FictiveTask();
			var task2 : ITask = new FictiveTask();
			
			sequence.addTask(task1);
			sequence.addTask(task2);
			
			var newTask : ITask;
			
			newTask = sequence.getTaskBefore(task2);
			
			assertEquals("these tasks should be equal", task1, newTask);
		}
		
		/* public function testGetTaskBeforeNonExistentOne() : void
		{
			var sequence : TaskSequence = new TaskSequence();
			
			var task1 : ITask = new FictiveTask();
			var task2 : ITask = new FictiveTask();
			
			sequence.addTask(task1);
			sequence.addTask(task2);
			
			var newTask : ITask;
			var nonExistentTask : ITask = new FictiveTask();
			
			newTask = sequence.getTaskBefore(nonExistentTask);
			
			assertUndefined("expected result should be undefined", newTask);
		} */
		
		public function testNestedTaskSequenceCompletion() : void 
		{
			var sequence : TaskSequence = new TaskSequence();
				
			var taskToCompleteAsync1 : ITask = new FictiveTask();
			var taskToCompleteAsync2 : ITask = new FictiveTask();
			
			var task1CompletedEventDescription : EventDescriptor =
				new EventDescriptor(taskToCompleteAsync1, TaskEvent.COMPLETED);
			
			var task2CompletedEventDescription : EventDescriptor =
				new EventDescriptor(taskToCompleteAsync2, TaskEvent.COMPLETED);
			
			var asyncTask1 : ITask = 
				ExecuteAsyncMethodTask.create(task1CompletedEventDescription, emptyMethod, [], this);
			
			var asyncTask2 : ITask = 
				ExecuteAsyncMethodTask.create(task2CompletedEventDescription, emptyMethod, [], this);
			
			var sequenceTask : TaskSequence = new TaskSequence();
			
			var taskToCompleteAsync3 : ITask = new FictiveTask();
			var task3CompletedEventDescription : EventDescriptor = 
				new EventDescriptor(taskToCompleteAsync3, TaskEvent.COMPLETED);
			
			var taskToCompleteAsync4 : ITask = new FictiveTask();
			var task4CompletedEventDescriptor : EventDescriptor =
				new EventDescriptor(taskToCompleteAsync4, TaskEvent.COMPLETED);
			
			var asyncTask3 : ITask = 
				ExecuteAsyncMethodTask.create(task3CompletedEventDescription, emptyMethod, [], this);
			
			var asyncTask4 : ITask = 
				ExecuteAsyncMethodTask.create(task4CompletedEventDescriptor, emptyMethod, [], this);
				
			sequenceTask.addTask(asyncTask3);
			sequenceTask.addTask(asyncTask4);
				
			sequence.addTask(asyncTask1);
			sequence.addTask(sequenceTask);
			sequence.addTask(asyncTask2);	
				
			sequence.start();
			
			assertTrue("Sequence should be running after start", sequence.isRunning());
			assertTrue("asyncTask1 should be running", asyncTask1.isRunning());
			assertFalse("asyncTask2 should not be running", asyncTask2.isRunning());
			assertFalse("asyncTask3 should not be running", asyncTask3.isRunning());
			assertFalse("asyncTask4 should not be running", asyncTask4.isRunning());
			assertFalse("sequenceTask should not be running", sequenceTask.isRunning());
								
			taskToCompleteAsync1.start();
			
			assertTrue("Sequence should be still running after asyncTask1 completed", sequence.isRunning());
			assertTrue("sequenceTask should be running", sequenceTask.isRunning());
			assertFalse("asyncTask1 should not be running", asyncTask1.isRunning());
			assertTrue("asyncTask3 should be running", asyncTask3.isRunning());
			assertFalse("asyncTask2 should not be running", asyncTask2.isRunning());
			assertFalse("asyncTask4 should not be running", asyncTask4.isRunning());
	
			taskToCompleteAsync3.start();			
			
			assertTrue("Sequence should be still running after asyncTask3 completed", sequence.isRunning());
			assertTrue("sequenceTask should be running", sequenceTask.isRunning());
			assertFalse("asyncTask1 should not be running", asyncTask1.isRunning());
			assertTrue("asyncTask4 should be running", asyncTask4.isRunning());
			assertFalse("asyncTask3 should not be running", asyncTask3.isRunning());
			assertFalse("asyncTask2 should not be running", asyncTask2.isRunning());
				
			taskToCompleteAsync4.start();
			
			assertTrue("Sequence should be still running after asyncTask3 completed", sequence.isRunning());
			assertFalse("sequenceTask should not be running", sequenceTask.isRunning());
			assertFalse("asyncTask1 should not be running", asyncTask1.isRunning());
			assertTrue("asyncTask2 should be running", asyncTask2.isRunning());
			assertFalse("asyncTask3 should not be running", asyncTask3.isRunning());
			assertFalse("asyncTask4 should not be running", asyncTask4.isRunning());
				
			taskToCompleteAsync2.start();			
			
			assertFalse("Sequence should be still running after asyncTask3 completed", sequence.isRunning());
			assertFalse("sequenceTask should not be running", sequenceTask.isRunning());
			assertFalse("asyncTask1 should not be running", asyncTask1.isRunning());
			assertFalse("asyncTask2 should be running", asyncTask2.isRunning());
			assertFalse("asyncTask3 should not be running", asyncTask3.isRunning());
			assertFalse("asyncTask4 should not be running", asyncTask4.isRunning());
			
		}
		
		public function testRemoveTaskWhenNotRunning() : void
		{
			var taskSequence : TaskSequence = new TaskSequence();
			var tasks : Array;
						
			taskSequence.addEventListener(TasksContainerEvent.TASK_ADDED, onTaskAdded);
			taskSequence.addEventListener(TasksContainerEvent.TASK_REMOVED, onTaskRemoved);
			
			var fictiveTask1 : FictiveTask = new FictiveTask();
			var fictiveTask2 : FictiveTask = new FictiveTask();
			
			taskSequence.addTask(fictiveTask1);
			taskSequence.addTask(fictiveTask2);
			
			taskSequence.removeTask(fictiveTask1);
			
			tasks = taskSequence.getAllTasks();
			assertEquals("taskSequence should contain only 1 tasks after this removeTask()", 1, tasks.length);
			
			taskSequence.removeTask(fictiveTask2);
				
			tasks = taskSequence.getAllTasks();
			assertEquals("taskSequence should contain no tasks after this removeTask()", 0,	tasks.length);
			
			assertEquals("taskSequence.addTask should be called 2 times", 2, mTaskAddedCounter);
			assertEquals("taskSequence.removeTask shoulde be called 2 times", 2, mTaskRemovedCounter);				
		}
		
		/*  
		public function testRemoveRunningTask() : void
		{
			var fictiveEventSource : FictiveTask = new FictiveTask();
			var eventDescriptor : EventDescriptor = new EventDescriptor(fictiveEventSource, TaskEvent.COMPLETED);
			var asyncTask : ExecuteAsyncMethodTask = 
				ExecuteAsyncMethodTask.create(eventDescriptor, emptyMethod, [], this);
			
			mTaskSequence.addEventListener(TasksContainerEvent.TASK_REMOVED, onTaskRemoved);
			mTaskSequence.addEventListener(TaskEvent.START, addAsync(assertIsRunningAndTestRemoveRunningTask, 300));
					
			mTaskSequence.addTask(asyncTask);
			
			mTaskSequence.start();
		}
		
		private function assertIsRunningAndTestRemoveRunningTask(aEvent : Event) : void
		{
			assertTrue("taskSequence should be started and running now", mTaskSequence.isRunning());
				
			// try to remove the running task:
			// assertThrows(
				//	"Can not delete currently running task", 
				//	mTaskSequence, 
				//	mTaskSequence.removeTask, 
				//	[asyncTask]); 
			
			var tasks:Array = mTaskSequence.getAllTasks();
			assertEquals(
					"taskSequence should still contain asyncTask after this removeTask(asyncTask) - asyncTask is still running", 
					1, 
					tasks.length);			
		} */
		
		public function testInterruptBeforeStart() : void
		{
			var fictiveTask : FictiveTask = new FictiveTask();
			
			var taskSequence : TaskSequence = new TaskSequence();
			taskSequence.addEventListener(TaskEvent.INTERRUPTED, onTaskInterrupted);
			
			taskSequence.addTask(fictiveTask);
			
			taskSequence.interrupt();
			
			assertTrue("taskSequence should be interrupted", mTaskInterrupted);			
		}

		public function testInterruptAfterCompleted() : void
		{
			var fictiveTask:FictiveTask = new FictiveTask();
			fictiveTask.addEventListener(TaskEvent.INTERRUPTED, onIndividualTaskInterrupted);			
			
			mTaskSequence.addTask(fictiveTask);
			
			mTaskSequence.addEventListener(TaskEvent.COMPLETED, addAsync(assertCompletedAndInterrupt, 300));
			mTaskSequence.addEventListener(TaskEvent.INTERRUPTED, onTaskInterrupted);
			
			mTaskSequence.start();			
		}
		private var mIndividualTaskInterrupted : Boolean = false;
		private function onIndividualTaskInterrupted(aEvent : Event) : void
		{
			mIndividualTaskInterrupted = true;
		}
		
		private function onTaskInterrupted(aEvent : Event) : void
		{
			mTaskInterrupted = true;
		}
		
		private function assertCompletedAndInterrupt(aEvent : Event) : void
		{
			var task : ITask = ITask(aEvent.target);
			
			assertFalse("Task should be completed", task.isRunning());

			task.interrupt();
			
			assertTrue("TaskSequence should be interrupted", mTaskInterrupted);
			assertFalse("Task should not be interrupted", mIndividualTaskInterrupted)
		}
		
		private var mFictiveEventSource : FictiveTask;
		public function testInterruptWhenRunning() : void
		{
			mFictiveEventSource = new FictiveTask();
			var eventDescriptor:EventDescriptor = new EventDescriptor(mFictiveEventSource,TaskEvent.COMPLETED);
			var asyncTask:ExecuteAsyncMethodTask = 
				ExecuteAsyncMethodTask.create(eventDescriptor, emptyMethod,	[], this);
				
			asyncTask.addEventListener(TaskEvent.INTERRUPTED, onIndividualTaskInterrupted);

			mTaskSequence.addEventListener(TaskEvent.START, addAsync(assertTaskSequenceIsRunningAndInterrupt, 200));
			mTaskSequence.addEventListener(TaskEvent.INTERRUPTED, onTaskInterrupted);
					
			mTaskSequence.addTask(asyncTask);
			
			mTaskSequence.start();
		}
		
		private function assertTaskSequenceIsRunningAndInterrupt(aEvent : Event) : void
		{
			var task : ITask = ITask(aEvent.target);
			
			assertTrue("TaskSequence should be running", task.isRunning());
			
			task.interrupt();
			// this is needed in order to interrupt asyncTask:
			mFictiveEventSource.start();
			
			assertTrue("Fictive task should be interrupted", mIndividualTaskInterrupted);
			assertTrue("TaskSequence should be interrupted", mTaskInterrupted)
			
			assertFalse("TaskSequence should not be running", task.isRunning());
		}
		
		public function testIsImmediatelyInterruptableBeforeStart() : void
		{
			var taskSequence:TaskSequence = new TaskSequence();
			
			assertTrue(
					"taskSequence should be immediately interruptable before start()", 
					taskSequence.isImmediatelyInterruptable());
		}
		
		public function testIsImmediatelyInterruptableAfterCompleted() : void
		{
			var fictiveTask:FictiveTask = new FictiveTask();
			
			mTaskSequence.addEventListener(TaskEvent.COMPLETED, assertCompletedAndTestIsImmediatelyInterruptable);
			
			mTaskSequence.addTask(fictiveTask);
		
			mTaskSequence.start();	
		}		
		
		private function assertCompletedAndTestIsImmediatelyInterruptable(aEvent : Event) : void
		{
			var task : ITask = ITask(aEvent.target);
			
			assertFalse("Task should be completed", task.isRunning());
			
			assertTrue("Task should be immediately interruptable after completed", task.isImmediatelyInterruptable());
		}
		
		public function testIsImmediatelyInterruptableWhenRunningAsyngTask() : void
		{
			var fictiveEventSource:FictiveTask = new FictiveTask();
			var eventDescriptor:EventDescriptor = new EventDescriptor(fictiveEventSource, TaskEvent.COMPLETED);
			var asyncTask:ExecuteAsyncMethodTask = 
				ExecuteAsyncMethodTask.create(eventDescriptor, emptyMethod, [], this);
			
			mTaskSequence.addEventListener(TaskEvent.START, 
				addAsync(assertTaskSequenceIsRunningAndTestIsImmediatelyInterruptable, 300));
					
			mTaskSequence.addTask(asyncTask);
			
			mTaskSequence.start();			
		}
		
		private function assertTaskSequenceIsRunningAndTestIsImmediatelyInterruptable(aEvent : Event) : void
		{
			var task : TaskSequence = TaskSequence(aEvent.target);
			
			assertTrue("taskSequence should be in running state now", task.isRunning());
				
			assertEquals(
					"taskSequence.isImmediatelyInterruptable() should equals now asyncTask.isImmediatelyInterruptable()", 
					task.isImmediatelyInterruptable(), 
					task.CurrentTask.isImmediatelyInterruptable());
		}
		
		public function testIsImmediatelyInterruptableAfterInterrupt() : void
		{
			mFictiveEventSource = new FictiveTask();
			var eventDescriptor:EventDescriptor = new EventDescriptor(mFictiveEventSource, TaskEvent.COMPLETED);
			var asyncTask:ExecuteAsyncMethodTask = 
				ExecuteAsyncMethodTask.create(eventDescriptor, emptyMethod, [], this);
				
			asyncTask.addEventListener(TaskEvent.INTERRUPTED, onIndividualTaskInterrupted);
			
			mTaskSequence.addEventListener(TaskEvent.START, addAsync(assertTaskSequenceIsRunningInterruptAndTestIsImideatelyInterruptable, 200));
			mTaskSequence.addEventListener(TaskEvent.INTERRUPTED, onTaskInterrupted);
					
			mTaskSequence.addTask(asyncTask);
			
			mTaskSequence.start();			
		} 
		
		private function assertTaskSequenceIsRunningInterruptAndTestIsImideatelyInterruptable(aEvent : Event) : void
		{
			var task : ITask = ITask(aEvent.target);
			
			assertTrue("TaskSequence should be running", task.isRunning());
			
			task.interrupt();
			// this is needed in order to interrupt asyncTask:
			mFictiveEventSource.start();
			
			assertTrue("Fictive task should be interrupted", mIndividualTaskInterrupted);
			assertTrue("TaskSequence should be interrupted", mTaskInterrupted)
			
			assertFalse("TaskSequence should not be running", task.isRunning());
			
			assertFalse("Task should not be running", task.isRunning());
			assertTrue("TaskSequence should be ImmediatelyInterruptable after interrupt()", task.isImmediatelyInterruptable());
		}
		
		public function testMarkTask () : void
		{
			var sequence : TaskSequence = new TaskSequence();
			var task1 : ITask = new FictiveTask();
			var task2 : ITask = new FictiveTask();
			
			sequence.addTask(task1);
			sequence.addTask(task2);
			
			sequence.markTask(task2, "final");
			
			assertNotUndefined(
				"getMarkedTask(marker) should not be undefined",
				sequence.getMarkedTask("final")
			);
			
			assertEquals(
				"getMarkedTask(marker) should match the marked task ",
				sequence.getMarkedTask("final"),
				task2
			);
		}
		
		private function onTaskRemoved(aEvent : Event) : void
		{
			mTaskRemovedCounter++;
		}
		
		private function onTaskAdded(aEvent : TasksContainerEvent) : void
		{
			mTaskAddedCounter++;
		}
		
		private function onTaskSequenceStarted(aEvent : TaskEvent) : void
		{
			var task : ITask = ITask(aEvent.target);
			mTaskSequenceStarted = true;
			mTaskSequenceIsRunning = task.isRunning();
		}
		
		private function onTaskSequenceCompleted(aEvent : TaskEvent) : void
		{
			var task : ITask = ITask(aEvent.target);
			
			mTaskSequenceCompleted = true;
		}
		
		private function assertTaskSequenceCompleted(aEvent : TaskEvent) : void
		{
			var task : ITask = ITask(aEvent.target);
			mTaskSequenceCompleted = true;			
			assertFalse("TaskSequence should be completed and not running", task.isRunning());
		}
	}
}